package com.domino.client.mvc.impl.ai;

import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Map.Entry;
import java.util.Set;

import com.domino.shared.Player;
import com.domino.shared.PlayerId;
import com.domino.shared.model.Board;
import com.domino.shared.model.Brick;
import com.domino.shared.model.Color;
import com.domino.shared.model.Point;

public class Bettan implements Bot {
	
	private Player player = new Player(new PlayerId("client-bot"), "Bettan", null);
	private Color botColor;
	private Color humanColor;
	private Random rand = new Random(new Date().getTime());
	
	public Bettan(Color botColor) {
		this.botColor = botColor;
		this.humanColor = Color.getOpposite(botColor);
	}

	@Override
	public Player getPlayer() {
		return player;
	}

	@Override
	public Brick getNexMove(Board board) {
		// 1. give each availble move a point
		Set<Point> moves = board.getAvailblePoints(botColor);
		if (moves.isEmpty()) {
			throw new IllegalStateException();
		}
		
		Map<Point,Integer> ranks = new HashMap<Point,Integer>(moves.size());
		for(Point p : moves) {
			ranks.put(p, getRank(board, p));
		}
		// 2. select the highest, 15% chance of ignoring each entry
		Iterator<Entry<Point, Integer>> entries = ranks.entrySet().iterator();
		Map.Entry<Point, Integer> highest = entries.next();
		while(entries.hasNext()) {
			Map.Entry<Point, Integer> entry = entries.next();
			if (entry.getValue()>highest.getValue() && rand.nextInt(100)<15) {
				highest = entry;
			}
		}
		return new Brick(highest.getKey(), botColor);
	}

	private Integer getRank(Board board, Point p) {
		if (isCornerPoint(board,p)) {
			return 3;
		}
		if (isIsolatedOrAdjantenOwnBorderPoint(board,p)) {
			return 2;
		}
		if (isAdjatentOpponentBorderPoint(board,p)) {
			return -1;
		}
		if (isAdjatentCorner(board,p)) {
			return -2;
		}
		return 0;
	}

	private boolean isAdjatentOpponentBorderPoint(Board board, Point p) {
		int size = board.getSize();
		if (isX(p, 0) && isOpponent(board, p.getX(), p.getY()-1) && isNotOpponent(board, p.getX(), p.getY()+1)) {
			return  true;
		}
		if (isX(p, size-1) && isOpponent(board, p.getX(), p.getY()-1) && isNotOpponent(board, p.getX(), p.getY()+1)) {
			return  true;
		}
		if (isY(p, 0) && isOpponent(board, p.getX()-1, p.getY()) && isNotOpponent(board, p.getX()+1, p.getY())) {
			return  true;
		}
		if (isY(p, size-1) && isOpponent(board, p.getX()-1, p.getY()) && isNotOpponent(board, p.getX()+1, p.getY())) {
			return  true;
		}
		return false;
	}

	private boolean isIsolatedOrAdjantenOwnBorderPoint(Board board, Point p) {
		int size = board.getSize();
		if (isX(p, 0) && isNotOpponent(board, p.getX(), p.getY()-1) && isNotOpponent(board, p.getX(), p.getY()+1)) {
			return  true;
		}
		if (isX(p, size-1) && isNotOpponent(board, p.getX(), p.getY()-1) && isNotOpponent(board, p.getX(), p.getY()+1)) {
			return  true;
		}
		if (isY(p, 0) && isNotOpponent(board, p.getX()-1, p.getY()) && isNotOpponent(board, p.getX()+1, p.getY())) {
			return  true;
		}
		if (isY(p, size-1) && isNotOpponent(board, p.getX()-1, p.getY()) && isNotOpponent(board, p.getX()+1, p.getY())) {
			return  true;
		}
		return false;
	}

	private boolean isNotOpponent(Board board, int x, int y) {
		if (y<0 || y>=board.getSize()) {
			return true;
		}
		return humanColor.equals(board.getColor(new Point(x,y)))==false;
	}
	
	private boolean isOpponent(Board board, int x, int y) {
		if (y<0 || y>=board.getSize()) {
			return false;
		}
		return humanColor.equals(board.getColor(new Point(x,y)));
	}

	private boolean isAdjatentCorner(Board board, Point p) {
		int size = board.getSize();
		return isXY(p,1,1) || isXY(p,size-2,1) || isXY(p,1,size-2) || isXY(p,size-2,size-2);
	}

	private boolean isCornerPoint(Board board, Point p) {
		int size = board.getSize();
		return isXY(p,0,0) || isXY(p,size-1,0) || isXY(p,0,size-1) || isXY(p,size-1,size-1);
	}

	private boolean isXY(Point p, int x, int y) {
		return p.getX()==x && p.getY()==y;
	}

	private boolean isX(Point p, int x) {
		return p.getX()==x;
	}
	
	private boolean isY(Point p, int y) {
		return p.getY()==y;
	}
}
